{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Implementations of early and well-known poetry generators\n",
    "\n",
    "By [Allison Parrish](http://www.decontextualize.com/)\n",
    "\n",
    "This notebook has some Python implementations of a number of early and well-known poetry generators, including Knowles and Tenney's *A House of Dust*, Strachey's love letter generator and Nick Montfort's *Taroko Gorge*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## To Make a Dadaist Poem\n",
    "\n",
    "Original written by [Tristan Tzara](http://www.391.org/manifestos/1920-dada-manifesto-feeble-love-bitter-love-tristan-tzara.html#.WnPkJYJOndd) in 1920."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "more Dadaists participants, international World movement\n",
      "conformity corresponded participants beginnings The broadly\n",
      "war, with informal Europe an Dada North war. to For cause of\n",
      "War the of — cultural the movement, root the were was the\n",
      "many to and of in art the the outbreak America. a in against\n",
      "nationalist in against correspond and I. interests, — the\n",
      "believed and many was intellectual protest that society\n",
      "colonialist which and and Dada bourgeois\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "import textwrap\n",
    "\n",
    "newspaper = \"\"\"\n",
    "Dada was an informal international movement, with \n",
    "participants in Europe and North America. The \n",
    "beginnings of Dada correspond to the outbreak of \n",
    "World War I. For many participants, the movement \n",
    "was a protest against the bourgeois nationalist \n",
    "and colonialist interests, which many Dadaists \n",
    "believed were the root cause of the war, and \n",
    "against the cultural and intellectual \n",
    "conformity — in art and more broadly in \n",
    "society — that corresponded to the war.\"\"\"\n",
    "\n",
    "words = newspaper.split()\n",
    "random.shuffle(words)\n",
    "\n",
    "print(textwrap.fill(\" \".join(words), 60))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A House of Dust\n",
    "\n",
    "Original written in Fortran in 1967 by Alison Knowles and James Tenney. [ELMCIP entry](https://elmcip.net/creative-work/house-dust). [More information](http://blog.calarts.edu/2009/09/10/alison-knowles-james-tenney-and-the-house-of-dust-at-calarts/). [Watch Alison Knowles read from this piece](https://www.youtube.com/watch?v=-68Z708lFsY)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "materials = [\n",
    "    'brick',\n",
    "    'broken dishes',\n",
    "    'discarded clothing',\n",
    "    'dust',\n",
    "    'glass',\n",
    "    'leaves',\n",
    "    'mud',\n",
    "    'paper',\n",
    "    'plastic',\n",
    "    'roots',\n",
    "    'sand',\n",
    "    'steel',\n",
    "    'stone',\n",
    "    'straw',\n",
    "    'tin',\n",
    "    'weeds',\n",
    "    'wood'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "locations = [\n",
    "    'among high mountains',\n",
    "    'among other houses',\n",
    "    'among small hills',\n",
    "    'by a river',\n",
    "    'by an abandoned lake',\n",
    "    'by the sea',\n",
    "    'in a cold, windy climate',\n",
    "    'in a deserted airport',\n",
    "    'in a deserted church',\n",
    "    'in a deserted factory',\n",
    "    'in a green, mossy terrain',\n",
    "    'in a hot climate',\n",
    "    'in a metropolis',\n",
    "    'in a place with both heavy rain and bright sun',\n",
    "    'in an overpopulated area',\n",
    "    'in dense woods',\n",
    "    'in heavy jungle undergrowth',\n",
    "    'in japan',\n",
    "    'in michigan',\n",
    "    'in southern france',\n",
    "    'inside a mountain',\n",
    "    'on an island',\n",
    "    'on the sea',\n",
    "    'underwater'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "lights = [\n",
    "    'all available lighting',\n",
    "    'candles',\n",
    "    'electricity',\n",
    "    'natural light'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "inhabitants = [\n",
    "    'all races of men represented wearing predominantly red clothing',\n",
    "    'children and old people',\n",
    "    'collectors of all types',\n",
    "    'fishermen and families',\n",
    "    'french and german speaking people',\n",
    "    'friends',\n",
    "    'friends and enemies',\n",
    "    'horses and birds',\n",
    "    'little boys',\n",
    "    'lovers',\n",
    "    'people from many walks of life',\n",
    "    'people speaking many languages wearing little or no clothing',\n",
    "    'people who eat a great deal',\n",
    "    'people who enjoy eating together',\n",
    "    'people who love to read',\n",
    "    'people who sleep almost all the time',\n",
    "    'people who sleep very little',\n",
    "    'various birds and fish',\n",
    "    'vegetarians',\n",
    "    'very tall people'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "A house of weeds\n",
      "     in michigan\n",
      "          using candles\n",
      "                inhabited by people who enjoy eating together\n",
      "\n",
      "A house of brick\n",
      "     among small hills\n",
      "          using natural light\n",
      "                inhabited by friends and enemies\n",
      "\n",
      "A house of straw\n",
      "     in japan\n",
      "          using natural light\n",
      "                inhabited by collectors of all types\n",
      "\n",
      "A house of roots\n",
      "     on the sea\n",
      "          using electricity\n",
      "                inhabited by people who sleep very little\n",
      "\n",
      "A house of mud\n",
      "     in a green, mossy terrain\n",
      "          using all available lighting\n",
      "                inhabited by various birds and fish\n",
      "\n",
      "A house of tin\n",
      "     in a place with both heavy rain and bright sun\n",
      "          using candles\n",
      "                inhabited by people who love to read\n",
      "\n",
      "A house of steel\n",
      "     in a deserted airport\n",
      "          using electricity\n",
      "                inhabited by little boys\n"
     ]
    }
   ],
   "source": [
    "stanza_count = 7\n",
    "for i in range(stanza_count):\n",
    "    print()\n",
    "    print(\"A house of \" + random.choice(materials))\n",
    "    print(\"     \" + random.choice(locations))\n",
    "    print(\"          using \" + random.choice(lights))\n",
    "    print(\"                inhabited by \" + random.choice(inhabitants))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Love Letter Generator\n",
    "\n",
    "Original by Christopher Strachey, written for the Manchester Mark I in 1952. [Read more here](https://grandtextauto.soe.ucsc.edu/2005/08/01/christopher-strachey-first-digital-artist/).\n",
    "\n",
    "Vocabulary based on [this implementation](https://github.com/gingerbeardman/loveletter/blob/master/index.php)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "sal_adjs = [\n",
    "    \"Beloved\",\n",
    "    \"Darling\",\n",
    "    \"Dear\",\n",
    "    \"Dearest\",\n",
    "    \"Fanciful\",\n",
    "    \"Honey\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "sal_nouns = [\n",
    "    \"Chickpea\",\n",
    "    \"Dear\",\n",
    "    \"Duck\",\n",
    "    \"Jewel\",\n",
    "    \"Love\",\n",
    "    \"Moppet\",\n",
    "    \"Sweetheart\"\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "adjs = [\n",
    "    'affectionate',\n",
    "    'amorous',\n",
    "    'anxious',\n",
    "    'avid',\n",
    "    'beautiful',\n",
    "    'breathless',\n",
    "    'burning',\n",
    "    'covetous',\n",
    "    'craving',\n",
    "    'curious',\n",
    "    'eager',\n",
    "    'fervent',\n",
    "    'fondest',\n",
    "    'loveable',\n",
    "    'lovesick',\n",
    "    'loving',\n",
    "    'passionate',\n",
    "    'precious',\n",
    "    'seductive',\n",
    "    'sweet',\n",
    "    'sympathetic',\n",
    "    'tender',\n",
    "    'unsatisfied',\n",
    "    'winning',\n",
    "    'wistful'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "nouns = [\n",
    "    'adoration',\n",
    "    'affection',\n",
    "    'ambition',\n",
    "    'appetite',\n",
    "    'ardour',\n",
    "    'being',\n",
    "    'burning',\n",
    "    'charm',\n",
    "    'craving',\n",
    "    'desire',\n",
    "    'devotion',\n",
    "    'eagerness',\n",
    "    'enchantment',\n",
    "    'enthusiasm',\n",
    "    'fancy',\n",
    "    'fellow feeling',\n",
    "    'fervour',\n",
    "    'fondness',\n",
    "    'heart',\n",
    "    'hunger',\n",
    "    'infatuation',\n",
    "    'little liking',\n",
    "    'longing',\n",
    "    'love',\n",
    "    'lust',\n",
    "    'passion',\n",
    "    'rapture',\n",
    "    'sympathy',\n",
    "    'thirst',\n",
    "    'wish',\n",
    "    'yearning'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "advs = [\n",
    "    'affectionately',\n",
    "    'ardently',\n",
    "    'anxiously',\n",
    "    'beautifully',\n",
    "    'burningly',\n",
    "    'covetously',\n",
    "    'curiously',\n",
    "    'eagerly',\n",
    "    'fervently',\n",
    "    'fondly',\n",
    "    'impatiently',\n",
    "    'keenly',\n",
    "    'lovingly',\n",
    "    'passionately',\n",
    "    'seductively',\n",
    "    'tenderly',\n",
    "    'wistfully'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "verbs = [\n",
    "    'adores',\n",
    "    'attracts',\n",
    "    'clings to',\n",
    "    'holds dear',\n",
    "    'hopes for',\n",
    "    'hungers for',\n",
    "    'likes',\n",
    "    'longs for',\n",
    "    'loves',\n",
    "    'lusts after',\n",
    "    'pants for',\n",
    "    'pines for',\n",
    "    'sighs for',\n",
    "    'tempts',\n",
    "    'thirsts for',\n",
    "    'treasures',\n",
    "    'yearns for',\n",
    "    'woos'\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dearest Love,\n",
      "\n",
      "You are my lovesick fellow feeling. My ardour tempts your\n",
      "sympathetic affection. My lust fervently hungers for your\n",
      "unsatisfied desire. My fondness anxiously lusts after your\n",
      "fellow feeling. You are my precious yearning.\n",
      "\n",
      "Yours beautifully,\n",
      "M.U.C.\n"
     ]
    }
   ],
   "source": [
    "# textwrap library used to \"wrap\" the text at a particular length\n",
    "import textwrap\n",
    "\n",
    "# output begins with salutation\n",
    "output = random.choice(sal_adjs) + \" \" + random.choice(sal_nouns) + \",\\n\"\n",
    "output += \"\\n\"\n",
    "\n",
    "# inside this loop, build the phrases. strachey implemented \"short\" phrases\n",
    "# and \"long\" phrases; two or more \"short\" phrases in a row have special\n",
    "# formatting rules, so we need to know what the last phrase kind was in\n",
    "# order to generate the output.\n",
    "history = []\n",
    "body = \"\"\n",
    "for i in range(5):\n",
    "    kind = random.choice([\"short\", \"long\"])\n",
    "    if kind == \"long\":\n",
    "        # adjectives and adverbs will be present only 50% of the time\n",
    "        line = \" \".join([\n",
    "            \"My\",\n",
    "            random.choice([random.choice(adjs), \"\"]),\n",
    "            random.choice(nouns),\n",
    "            random.choice([random.choice(advs), \"\"]),\n",
    "            random.choice(verbs),\n",
    "            \"your\",\n",
    "            random.choice([random.choice(adjs), \"\"]),\n",
    "            random.choice(nouns)])\n",
    "        body += line\n",
    "    else:\n",
    "        adj_noun = random.choice(adjs) + \" \" + random.choice(nouns)\n",
    "        # if the last phrase was \"short,\" use truncated form\n",
    "        if len(history) > 0 and history[-1] == \"short\":\n",
    "            body += \": my \" + adj_noun\n",
    "        else:\n",
    "            body += \"You are my \" + adj_noun\n",
    "    body += \". \"\n",
    "    history.append(kind)\n",
    "# clean up output\n",
    "body = body.replace(\"  \", \" \")\n",
    "body = body.replace(\". :\", \":\")\n",
    "# put everything together\n",
    "output += textwrap.fill(body, 60)\n",
    "output += \"\\n\\nYours \" + random.choice(advs) + \",\\n\"\n",
    "output += \"M.U.C.\"\n",
    "print(output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Taroko Gorge\n",
    "\n",
    "[Original](http://nickm.com/taroko_gorge/) by [Nick Montfort](http://nickm.com/). [ELMCIP entry here](https://elmcip.net/creative-work/taroko-gorge)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "above = ['brow', 'mist', 'shape', 'layer', 'the crag', 'stone', 'forest', 'height']\n",
    "below = ['flow', 'basin', 'shape', 'vein', 'rippling', 'stone', 'cove', 'rock']\n",
    "transitive = ['command', 'pace', 'roam', 'trail', 'frame', 'sweep', 'exercise', 'range']\n",
    "imperative = ['track', 'shade', 'translate', 'stamp', 'progress through', 'direct', 'run', 'enter']\n",
    "intransitive = ['linger', 'dwell', 'rest', 'relax', 'hold', 'dream', 'hum']\n",
    "texture = ['rough', 'fine']\n",
    "adjectives = ['encompassing', 'sinuous', 'straight', 'objective', 'arched', 'cool', 'clear', 'dim', 'driven']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def path():\n",
    "    plural = random.sample([\"s\", \"\"], k=2)\n",
    "    words = random.choice(above)\n",
    "    if words == \"forest\" and random.randrange(4) == 0:\n",
    "        words = \"monkeys\" + \" \" + random.choice(transitive)\n",
    "    else:\n",
    "        words += plural[0] + \" \" + random.choice(transitive) + plural[1]\n",
    "    words += \" the \" + random.choice(below) + random.choice([\"s\", \"\"]) + \".\"\n",
    "    return words.capitalize()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cave():\n",
    "    adjs = adjectives[:] + random.sample(texture, 1)\n",
    "    return \"  \" + random.choice(imperative) + \" \" + \\\n",
    "        \" \".join(random.sample(adjs, random.randrange(1, 4))) + \" —\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def site():\n",
    "    if random.randrange(2) == 0:\n",
    "        words = random.choice(above)\n",
    "    else:\n",
    "        words = random.choice(below)\n",
    "    words += \"s \" + random.choice(intransitive) + \".\"\n",
    "    return words.capitalize()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Layer frames the ripplings.\n",
      "Stones relax.\n",
      "Mist trails the rocks.\n",
      "\n",
      "  translate rough —\n",
      "\n",
      "Heights roam the rocks.\n",
      "Stones dream.\n",
      "Stones dream.\n",
      "Shape frames the coves.\n",
      "\n",
      "  progress through sinuous fine dim —\n",
      "\n",
      "Brows trail the shapes.\n",
      "Stone trails the flow.\n",
      "\n",
      "  run encompassing —\n",
      "\n",
      "Stones command the stones.\n",
      "Brow sweeps the stone.\n",
      "\n",
      "  progress through sinuous cool dim —\n",
      "\n",
      "Mists command the coves.\n",
      "Veins hum.\n",
      "Shapes dream.\n",
      "Shapes pace the vein.\n",
      "\n",
      "  stamp driven objective encompassing —\n",
      "\n",
      "Forests range the cove.\n",
      "Flows rest.\n",
      "Forests hold.\n",
      "Mists frame the veins.\n",
      "\n",
      "  enter driven —\n",
      "\n",
      "Height paces the flow.\n",
      "Ripplings dwell.\n",
      "Brow commands the stones.\n",
      "\n",
      "  enter fine —\n",
      "\n",
      "Brows roam the flow.\n",
      "Basins hum.\n",
      "Shape frames the veins.\n",
      "\n",
      "  progress through clear —\n",
      "\n",
      "The crag exercises the flow.\n",
      "Forests rest.\n",
      "Layer frames the flows.\n",
      "\n",
      "  translate encompassing objective sinuous —\n",
      "\n",
      "Heights sweep the coves.\n",
      "Shapes trail the stones.\n",
      "\n",
      "  stamp driven —\n",
      "\n"
     ]
    }
   ],
   "source": [
    "stanza_count = 10\n",
    "for repeat in range(stanza_count):\n",
    "    line_count = random.randrange(3, 6)\n",
    "    for i in range(line_count):\n",
    "        if i == 0:\n",
    "            print(path())\n",
    "        elif i == line_count - 2:\n",
    "            print(path())\n",
    "        elif i == line_count - 1:\n",
    "            print()\n",
    "            print(cave())\n",
    "            print()\n",
    "        else:\n",
    "            print(site())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
